from CvPythonExtensions import *

import GerikSpells
from GerikSpells import GerikSpellBase
from GSGroupPolicies import GSAll as GSGroup
from GSHandlerPolicies import GSTargetting as GSHandler
from GSResultsPolicies import GSMission as GSResults

from GSTargetInfoPolicies import GSSinglePlotTarget
from GSTargetInfoPolicies import GSSquarePlotTarget
from CivcraftPolicies import CivcraftSpecificUnitTargetPolicy as GSSpecificUnitTarget

from CivcraftPolicies import CivcraftSpellPlotGraphicsPolicy as GSPlot
from GSGraphicsPolicies import GSPieMenu

import GerikSpellsUtils

from CivcraftSCUtils import checkAndRemoveEnergy
from CivcraftSCUtils import showNotEnoughEnergyMsg
from CivcraftSCUtils import getEnergyUsageString
from CivcraftSCUtils import isHallucination

gc = CyGlobalContext()
localtext = CyTranslator()

class HighTemplarBase(GerikSpellBase, GSGroup, GSHandler):
    def __init__(self):
        GerikSpellBase.__init__(self)
        GSGroup.__init__(self)
        GSHandler.__init__(self)


        self.m_iHighTemplarType = gc.getInfoTypeForString("UNIT_HIGH_TEMPLAR")
            
    def canUnitCast(self, pCastingUnit, pPlot, bVisible):
        if not pCastingUnit.getUnitType() == self.m_iHighTemplarType:
            return False

        if isHallucination(pCastingUnit):
            return False

        # Show this for all high templar.
        if bVisible:
            return True

        return True

class PsionicStorm(HighTemplarBase, GSResults):
    def __init__(self):
        HighTemplarBase.__init__(self)

        # Constants
        self.m_iStormEnergyCost = 75
        self.m_iStormSize = 3
        self.m_iStormRange = 5
        self.m_iStormResearchNeeded = gc.getInfoTypeForString("PROJECT_PROTOSS_HIGH_TEMPLAR_STORM")
        self.m_iStormResearchAt = gc.getProjectInfo(self.m_iStormResearchNeeded).getBuildingToBuildAt()
        self.m_iStormPlotAttachment = gc.getInfoTypeForString("PLOT_ATTACHMENT_PSIONIC_STORM")
        self.m_szNotValidText = localtext.getText( "TXT_KEY_CIVCRAFT_REQUIRES_RESEARCH_AT", (gc.getProjectInfo(self.m_iStormResearchNeeded).getDescription(),
                                                                                             gc.getBuildingInfo(self.m_iStormResearchAt).getDescription()) )

        # Policies
        pSquarePlotTargetPolicy = GSSquarePlotTarget(self.canTargetPlot, self.m_iStormSize)
        GSResults.__init__(self, pSquarePlotTargetPolicy)

    def getHelpText(self, pGroup, pPlot):
        szText = getEnergyUsageString(self.m_iStormEnergyCost)

        bValid = self.canGroupCast(pGroup, pPlot, False)
        if not bValid:
            szText += self.m_szNotValidText
        else:
            szText += gc.getSpellInfo(self.getSpellNumber()).getHelp()

        return szText

    def canUnitCast(self, pCastingUnit, pPlot, bVisible):
        if not HighTemplarBase.canUnitCast(self, pCastingUnit, pPlot, bVisible):
            return False

        if bVisible:
            return True

        # Check if they have the research and that the unit needs it.
        if not gc.getPlayer( pCastingUnit.getOwner() ).hasBuiltProject( self.m_iStormResearchNeeded ):
            return False

        return True

    def canTargetPlot(self, pCastingGroup, pCastingPlot, pTargetPlot, bVisible):
        return True

    def getRangePlotList(self, pCastingPlot):
        return GerikSpellsUtils.getRangePlotDistanceList(pCastingPlot, self.m_iStormRange)

    def createTargettingStageList(self, bAlt, bShift, bCtrl):
        pPlotTargetPolicy = GSSquarePlotTarget(self.canTargetPlot, self.m_iStormSize)
        pPlotTargetPolicy.setRangeMakerCallback(self.getRangePlotList)
        pPlotTargetPolicy.setCheckRange(True)

        pPlotGraphicsPolicy = GSPlot()
        pPlotGraphicsPolicy.setShowRangeGraphics(True)
        pPlotGraphicsPolicy.setCursorType("CURSOR_SINGLE_ATTACK")
        return [(pPlotTargetPolicy, pPlotGraphicsPolicy)]

    def startSpellResults(self, pCastingGroup, pTargetInfo):
        bShowNotEnoughEnergy = False
        
        iPlayer = pCastingGroup.getOwner()
        for pUnit in pCastingGroup.getUnits():
            if checkAndRemoveEnergy(pUnit, self.m_iStormEnergyCost):
                for pPlot in pTargetInfo.getAffectedTargetList(pCastingGroup, pCastingGroup.plot()):
                    pPlot.attachPlotAttachment(iPlayer, self.m_iStormPlotAttachment)
            else:
                bShowNotEnoughEnergy = True

        if bShowNotEnoughEnergy:
            showNotEnoughEnergyMsg(iPlayer)
            
class Hallucination(HighTemplarBase, GSResults):
    def __init__(self):
        HighTemplarBase.__init__(self)

        # Constants
        self.m_iHallucinationEnergyCost = 100
        self.m_iHallucinationRange = 4
        self.m_iHallucinationsCreated = 2
        self.m_iHallucinationUnitAttachment = gc.getInfoTypeForString("UNIT_ATTACHMENT_HALLUCINATION")
        self.m_iHallucinationResearchNeeded = gc.getInfoTypeForString("PROJECT_PROTOSS_HIGH_TEMPLAR_HALLUCINATION")
        self.m_iHallucinationResearchAt = gc.getProjectInfo(self.m_iHallucinationResearchNeeded).getBuildingToBuildAt()
        self.m_szNotValidText = localtext.getText( "TXT_KEY_CIVCRAFT_REQUIRES_RESEARCH_AT", (gc.getProjectInfo(self.m_iHallucinationResearchNeeded).getDescription(),
                                                                                             gc.getBuildingInfo(self.m_iHallucinationResearchAt).getDescription()) )

        # Policies
        pSpecificUnitTargetPolicy = GSSpecificUnitTarget(self.canTargetUnit)
        GSResults.__init__(self, pSpecificUnitTargetPolicy)

    def getHelpText(self, pGroup, pPlot):
        szText = getEnergyUsageString(self.m_iHallucinationEnergyCost)

        bValid = self.canGroupCast(pGroup, pPlot, False)
        if not bValid:
            szText += self.m_szNotValidText
        else:
            szText += gc.getSpellInfo(self.getSpellNumber()).getHelp()

        return szText

    def canUnitCast(self, pCastingUnit, pPlot, bVisible):
        if not HighTemplarBase.canUnitCast(self, pCastingUnit, pPlot, bVisible):
            return False

        if bVisible:
            return True

        # Check if they have the research and that the unit needs it.
        if not gc.getPlayer( pCastingUnit.getOwner() ).hasBuiltProject( self.m_iHallucinationResearchNeeded ):
            return False

        return True

    def canTargetPlot(self, pCastingGroup, pCastingPlot, pTargetPlot, bVisible):
        return True

    def canTargetUnit(self, pCastingGroup, pCastingPlot, pTargetUnit, bVisible):
        if bVisible:
            return True

        if pTargetUnit.isInvulnerable():
            return False

        if pTargetUnit.isDead():
            return False

        if pTargetUnit.getTotalNumUnitAttachments(self.m_iHallucinationUnitAttachment) > 0:
            return False

        return True

    def getRangePlotList(self, pCastingPlot):
        return GerikSpellsUtils.getRangePlotDistanceList(pCastingPlot, self.m_iHallucinationRange)

    def createTargettingStageList(self, bAlt, bShift, bCtrl):
        pStageList = []
        
        pPlotTargetPolicy = GSSinglePlotTarget(self.canTargetPlot)
        pPlotTargetPolicy.setRangeMakerCallback(self.getRangePlotList)
        pPlotTargetPolicy.setCheckRange(True)

        pPlotGraphicsPolicy = GSPlot()
        pPlotGraphicsPolicy.setShowRangeGraphics(True)
        pPlotGraphicsPolicy.setCursorType("CURSOR_SINGLE_ATTACK")

        pStageList.append( (pPlotTargetPolicy, pPlotGraphicsPolicy) )

        pUnitTargetPolicy = GSSpecificUnitTarget(self.canTargetUnit)
        pUnitTargetPolicy.setInvalidTargetDescriptionCallback(self.getInvalidTargetText)
        pUnitMenuPolicy = GSPieMenu()
        pStageList.append( (pUnitTargetPolicy, pUnitMenuPolicy) )

        return pStageList

    def getInvalidTargetText(self, pTargetUnit, bStrategyText):
        szText = u""
        pCastingGroup = self.getCastingGroup()

        if pTargetUnit.isInvulnerable():
            szText += u"\n"
            szText += localtext.getText("TXT_KEY_INVALID_TARGET_UNIT_INULNERABLE", ())

        if pTargetUnit.getTotalNumUnitAttachments(self.m_iHallucinationUnitAttachment) > 0:
            szText += u"\n"
            szText += localtext.getText("TXT_KEY_INVALID_TARGET_CANNOT_MAKE_HALLUCINATION_FROM_HALLUCINATION", ())

        return szText

    def startSpellResults(self, pCastingGroup, pTargetInfo):
        bShowNotEnoughEnergy = False
        
        iPlayer = pCastingGroup.getOwner()
        pCastingPlayer = gc.getPlayer(iPlayer)
        for pUnit in pCastingGroup.getUnits():
            if checkAndRemoveEnergy(pUnit, self.m_iHallucinationEnergyCost):
                for pLoopUnit in pTargetInfo.getAffectedTargetList(pCastingGroup, pCastingGroup.plot()):
                    iUnitX = pLoopUnit.getX()
                    iUnitY = pLoopUnit.getY()
                    for iI  in range(self.m_iHallucinationsCreated):
                        pHallucinatedUnit = pCastingPlayer.initUnit(pLoopUnit.getUnitType(), iUnitX, iUnitY, UnitAITypes.NO_UNITAI)
                        # Because the unit may start with the cloak provider ability, we have to remove it's sight (which will remove
                        # the cloak providing) before giving it the hallucination ability. Otherwise, it will change all the surrounding
                        # plots by one, and when the hallucination attachment is added, it will never again remove those increments,
                        # leaving plots with perpetual cloaking fields.
                        # The sight is given back to it after it becomes a hallucination, which restores it's sight as well.
                        pHallucinatedUnit.removeSight(pHallucinatedUnit.plot())
                        pHallucinatedUnit.attachUnitAttachment(iPlayer, self.m_iHallucinationUnitAttachment)
                        pHallucinatedUnit.returnSight(pHallucinatedUnit.plot())
                        
            else:
                bShowNotEnoughEnergy = True

        if bShowNotEnoughEnergy:
            showNotEnoughEnergyMsg(iPlayer)

                        
            
        
            
            
